package com.ddone.finance.service.impl;

import com.ddone.base.util.IpUtils;
import com.ddone.base.util.JwtUtils;
import com.ddone.common.exception.BusinessException;
import com.ddone.common.result.ResponseEnum;
import com.ddone.common.util.HttpClientUtils;
import com.ddone.finance.config.WxLoginProperties;
import com.ddone.finance.pojo.entity.UserInfo;
import com.ddone.finance.service.UserInfoService;
import com.ddone.finance.service.UserLoginRecordService;
import com.ddone.finance.service.WxLoginService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.http.HttpRequest;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @author ddone
 * @date 2024/4/18-10:42
 */
@Service
@Slf4j
public class WxLoginServiceImpl implements WxLoginService {

    @Resource
    private WxLoginProperties wxLoginProperties;

    @Resource
    private ObjectMapper objectMapper;

    @Resource
    private UserInfoService userInfoService;

    @Resource
    private UserLoginRecordService userLoginRecordService;

    @Override
    public String getQrcodeUrl(HttpSession session) {
        String baseUrl = "https://open.weixin.qq.com/connect/qrconnect" +
                "?appid=%s" +
                "&redirect_uri=%s" +
                "&response_type=code" +
                "&scope=snsapi_login" +
                "&state=%s" +
                "#wechat_redirect";
        //处理回调url
        String redirectUri = "";
        try {
            redirectUri = URLEncoder.encode(wxLoginProperties.getRedirectUri(), "UTF-8");
        } catch (UnsupportedEncodingException e) {
            log.error(ExceptionUtils.getStackTrace(e));
            throw new BusinessException(ResponseEnum.URL_ENCODE_ERROR);
        }
        //处理state：生成随机数，存入session 防止CSRF跨站点攻击 参考2.4
        String state = UUID.randomUUID().toString();
        log.info("生成 state = " + state);
        session.setAttribute("wx_open_state", state);
        String qrcodeUrl = String.format(
                baseUrl,
                wxLoginProperties.getAppId(),
                redirectUri,
                state
        );
        return qrcodeUrl;
    }

    @Override
    public String callBack(String code, String state, HttpSession session, HttpServletRequest request) throws JsonProcessingException {
        //回调被拉起，并获得code和state参数
        if(StringUtils.isEmpty(code) || StringUtils.isEmpty(state) ){
            throw new BusinessException(ResponseEnum.ILLEGAL_CALLBACK_REQUEST_ERROR);
        }
        String sessionState = (String)session.getAttribute("wx_open_state");
        if(!state.equals(sessionState)){
            log.error("非法回调请求");
            throw new BusinessException(ResponseEnum.ILLEGAL_CALLBACK_REQUEST_ERROR);
        }
        //携带授权临时票据code，和appid以及appsecret请求access_token
        String accessTokenUrl = wxLoginProperties.getWxAccessTokenUrl();
        Map<String, String> accessTokenParam = new HashMap<>();
        accessTokenParam.put("appid", wxLoginProperties.getAppId());
        accessTokenParam.put("secret", wxLoginProperties.getAppSecret());
        accessTokenParam.put("code", code);
        accessTokenParam.put("grant_type", "authorization_code");
        HttpClientUtils client = new HttpClientUtils(accessTokenUrl, accessTokenParam);
        String result = "";
        try {
            //发送请求
            client.get();
            result = client.getContent();
            System.out.println("result = " + result);
        } catch (Exception e) {
            log.error("获取access_token失败");
            throw new BusinessException(ResponseEnum.FETCH_ACCESS_TOKEN_FAILD);
        }
        // Gson gson = new Gson();
        // HashMap<String, Object> resultMap = gson.fromJson(result, HashMap.class);
        Map<String, Object> resultMap = objectMapper.readValue(result,
                new TypeReference<Map<String, Object>>() {});
        //判断微信获取access_token失败的响应
        Object errCodeObj = resultMap.get("errcode");
        if(errCodeObj != null){
            String errMsg = (String)resultMap.get("errmsg");
            Double errCode = (Double)errCodeObj;
            log.error("获取access_token失败 - " + "message: " + errMsg + ", errcode: " + errCode);
            throw new BusinessException(ResponseEnum.FETCH_ACCESS_TOKEN_FAILD);
        }
        //微信获取access_token响应成功
        String accessToken = (String)resultMap.get("access_token");
        String openid = (String)resultMap.get("openid");
        log.info("accessToken = " + accessToken);
        log.info("openid = " + openid);
        //根据access_token获取微信用户的基本信息
        UserInfo userInfo = userInfoService.getByOpenId(openid);
        if(userInfo == null){
            userInfo = getUserInfoFromWx(accessToken,openid);
            userInfoService.save(userInfo);
            // 如果当前信息与上次用户更新时间差7天则重新更新数据
        }else if((System.currentTimeMillis() - userInfo.getUpdateTime().getTime())
                >= 7*24*60*60*1000 ) {
            userInfo = getUserInfoFromWx(accessToken,openid);
            userInfoService.updateById(userInfo);
        }
        String ip = IpUtils.getIpAddressAtService(request);
        userLoginRecordService.saveLoginLog(userInfo,ip);
        return JwtUtils.createToken(userInfo.getId(),
                userInfo.getNickName());
    }

    private UserInfo getUserInfoFromWx(String accessToken, String openid) throws JsonProcessingException {

        //向微信的资源服务器发起请求，获取当前用户的用户信息
        String baseUserInfoUrl = wxLoginProperties.getWxUserInfoUrl();
        Map<String, String> baseUserInfoParam = new HashMap<>();
        baseUserInfoParam.put("access_token", accessToken);
        baseUserInfoParam.put("openid", openid);
        HttpClientUtils client = new HttpClientUtils(baseUserInfoUrl, baseUserInfoParam);
        String resultUserInfo = null;
        try {
            client.get();
            resultUserInfo = client.getContent();
        } catch (Exception e) {
            log.error(ExceptionUtils.getStackTrace(e));
            throw new BusinessException(ResponseEnum.FETCH_USERINFO_ERROR);
        }
        // HashMap<String, Object> resultUserInfoMap = gson.fromJson(resultUserInfo, HashMap.class);
        Map<String, Object> resultUserInfoMap = objectMapper.readValue(resultUserInfo,
                new TypeReference<Map<String, Object>>() {});
        if(resultUserInfoMap.get("errcode") != null){
            log.error("获取用户信息失败" + "，message：" + resultUserInfoMap.get("errmsg"));
            throw new BusinessException(ResponseEnum.FETCH_USERINFO_ERROR);
        }
        String nickname = (String)resultUserInfoMap.get("nickname");
        String headimgurl = (String)resultUserInfoMap.get("headimgurl");
        UserInfo userInfo = UserInfo
                .builder()
                .openid(openid)
                .nickName(nickname)
                .userType(2)
                .name(nickname)
                .salt(UUID.randomUUID().toString().substring(0, 8))
                .headImg(headimgurl)
                .nickName(nickname)
                .build();
        return userInfo;
    }

}
